{
module CoreLanguageLexer  
open System
open System.Collections.Generic
open Microsoft.FSharp.Text.Lexing
open CoreLanguageParser

let keywords =
    [
        "loop", LOOP;
        "Loop", LOOP;
        "if", IF;
        "If", IF;
        "choose", CHOOSE;
        "Choose", CHOOSE;
        "else", ELSE;
        "else", ELSE;
        "or", OR;
        "Or", OR;
        "skip", SKIP;
        "Skip", SKIP;
        "Goto", GOTO;
        "goto", GOTO;
        "Label", LABEL;
        "label", LABEL;
    ] |> Map.ofList

let ops =
    [
        ":=",  ASSIGN;
        "+",   PLUS;
        "*",   TIMES;
    ] |> Map.ofList

(* internal lexer definitions *)
let pos = ref 0
let varIndex = ref 0
let varString = ref ""
let error = fun x -> printfn("%A\n") x
let lexeme = Lexing.LexBuffer<_>.LexemeString

(* hash table from string to int - to store the variables found during parse *)
(* the hash table indetifier *)
let varTable  = new Dictionary<string, int>()

(* if variable was already encountered - returns its integer index, 
   otherwise assigns variable with a fresh index and returns this index *)
let rec getVarIndex (varId : string, varTable : Dictionary<string, int>) =
      let newVarIndex(varId : string) =
           let _ = varIndex := !(varIndex : int ref) + 1
           let _ = (varTable.Add(varId, !varIndex))
           let _ = (varString := (!(varString : string ref) + varId + " = " + (!varIndex).ToString() + "\n"))
           getVarIndex(varId, varTable)
      if (varTable.ContainsKey(varId)) then varTable.[varId]
      else newVarIndex(varId) 
}   

let alpha       = ['a'-'z' 'A'-'Z']
let digit       = ['0'-'9']   
let identifier  = alpha(alpha|digit|['-' '_'])*
let labelId     = ':'(alpha+)
let whitespace  = [' ' '\t']
let newline     = "\n\r" | '\n' | '\r'
let operator    = ":=" | "+" | "*"
let comment     = '%'[^'\n']*'\n'

rule tokenize = parse   
| comment       { lexbuf.EndPos <- lexbuf.EndPos.NextLine; tokenize lexbuf; }
| whitespace    { tokenize lexbuf }
| newline       { lexbuf.EndPos <- lexbuf.EndPos.NextLine; tokenize lexbuf; }
| operator      { ops.[LexBuffer<_>.LexemeString lexbuf] }
| identifier    { match keywords.TryFind(lexeme lexbuf) with
                        | Some(token) -> token
                        | None -> VAR(getVarIndex((lexeme lexbuf), varTable)) }
| "0"           { ZERO }
| ';'           { SEMI }
| '('           { LPAREN }
| ')'           { RPAREN }
| '{'           { LBRACE }
| '}'           { RBRACE }
| eof           { EOF }
| labelId       { LABELID (lexeme lexbuf) }
| _             { failwithf "unrecognized input: '%s'" (lexeme lexbuf) }